#include "GL/GLContext.h"
#include "GL/OpenGL.h"
#include "Exception.h"
#include "Logger.h"
#include "Window.h"

NS_B2D_BEGIN

GLContext GLContext::SharedContext;

void getGLVersion(int& major, int& minor)
{
      // for all versions
      char* ver = (char*)glGetString(GL_VERSION); // ver = "3.2.0"

      major = ver[0] - '0';
      if( major >= 3)
      {
       // for GL 3.x
       glGetIntegerv(GL_MAJOR_VERSION, &major); // major = 3
       glGetIntegerv(GL_MINOR_VERSION, &minor); // minor = 2
      }
      else
      {
            minor = ver[2] - '0';
      }

      // GLSL
      ver = (char*)glGetString(GL_SHADING_LANGUAGE_VERSION); // ver = "1.50 NVIDIA via Cg compiler"
}

void GLContext::InitialiseShared(void)
{
#if B2D_PLATFORM == B2D_PLATFORM_WIN32
	SharedContext.hWND = CreateWindowA("STATIC", "", WS_POPUP | WS_DISABLED, 0, 0, 1, 1, NULL, NULL, GetModuleHandle(NULL), NULL);
    ShowWindow(SharedContext.hWND, SW_HIDE);
#endif
    SharedContext.createContext(WindowOptions());
}
void GLContext::DestroyShared(void)
{
	SharedContext.destroy();
#if B2D_PLATFORM == B2D_PLATFORM_WIN32
	DestroyWindow(SharedContext.hWND);
#endif
}

void GLContext::initialise(const BaseWindow& window, WindowOptions& opts)
{
#if B2D_PLATFORM == B2D_PLATFORM_WIN32
	hWND = static_cast<HWND>(window.getWindowHandle());
#endif
	createContext(opts);
}

void GLContext::createContext(WindowOptions& opts)
{
#if B2D_PLATFORM == B2D_PLATFORM_WIN32
	hDC = GetDC(hWND);

    PIXELFORMATDESCRIPTOR pfd;
    ZeroMemory(&pfd, sizeof(pfd));
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
	pfd.cColorBits = opts.colorBits;
	pfd.cDepthBits = opts.depthBits;
    pfd.iLayerType = PFD_MAIN_PLANE;

    int format = ChoosePixelFormat(hDC, &pfd);
	if( format == 0 || SetPixelFormat(hDC, format, &pfd) == FALSE )
	{
		B2D_THROW(Exception::GRAPHICS_API_EXCEPTION,
			"Could not set pixel description");
	}
	
	hGLRC = wglCreateContext(hDC);
	if( hGLRC == nullptr )
	{
		B2D_THROW(Exception::GRAPHICS_API_EXCEPTION,
			"Could not create OpenGL context.");
	}
	if( SharedContext.hGLRC != hGLRC )
		wglShareLists(hGLRC, SharedContext.hGLRC);
	makeCurrent();
#endif


	// Check GL version
	int major(0), minor(0);
    getGLVersion(major, minor);
	B2D_LOG_DEBUG("OpenGL version: %i.%i\n", major, minor);

	// Version must be > 2.1
	if( major < 2 || (major == 2 && minor < 1) )
	{
		string str = "OpenGL version " + std::to_string(major) + ". " + std::to_string(minor) + "is not supported. Must be at least 2.1.";
		B2D_THROW(Exception::GRAPHICS_API_EXCEPTION, str);
	}

	// Initialise glew
	glewExperimental = 1;
	GLenum glewResult = glewInit();
	if( glewResult != GLEW_OK )
	{
		B2D_THROW(Exception::GRAPHICS_API_EXCEPTION,
			"Could not initialise glew: "+ string((const char*)glewGetErrorString(glewResult) ));
	}

	// Check for necessary features
	if( false == (
		GLEW_ARB_vertex_shader &&
		GLEW_ARB_fragment_shader
		))
    {
		B2D_THROW(Exception::GRAPHICS_API_EXCEPTION,
			"Update OpenGL drivers! Can't run without shaders.");
    }

	B2D_LOG_DEBUG("OpenGL context created successfully.\n");
}

void GLContext::makeCurrent(void)
{
#if B2D_PLATFORM == B2D_PLATFORM_WIN32
	wglMakeCurrent(hDC, hGLRC);
#endif
}

void GLContext::destroy(void)
{
#if B2D_PLATFORM == B2D_PLATFORM_WIN32
	wglMakeCurrent(nullptr, nullptr);
    wglDeleteContext(hGLRC);
    ReleaseDC(hWND, hDC);

	hDC = nullptr;
	hGLRC = nullptr;
	hWND = 0;
#endif
	B2D_LOG_DEBUG("OpenGL context destroyed.\n");
}

void GLContext::swapBuffers(bool vSync)
{
#if B2D_PLATFORM == B2D_PLATFORM_WIN32
	SwapBuffers(hDC);
#endif
}


NS_B2D_END